package com.blkj.iam.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blkj.iam.common.constant.StationConstants;
import com.blkj.iam.common.constant.SystemConstants;
import com.blkj.iam.common.enums.StatusEnum;
import com.blkj.iam.common.exception.BusinessException;
import com.blkj.iam.common.model.Option;
import com.blkj.iam.common.result.ApiResult;
import com.blkj.iam.common.util.PinyinUtils;
import com.blkj.iam.core.security.util.SecurityUtils;
import com.blkj.iam.system.converter.DeptConverter;
import com.blkj.iam.system.mapper.DeptMapper;
import com.blkj.iam.system.mapper.RoleMapper;
import com.blkj.iam.system.mapper.UserMapper;
import com.blkj.iam.system.model.bo.UserBO;
import com.blkj.iam.system.model.entity.Dept;
import com.blkj.iam.system.model.entity.Role;
import com.blkj.iam.system.model.entity.User;
import com.blkj.iam.system.model.form.DeptForm;
import com.blkj.iam.system.model.query.DeptPageQuery;
import com.blkj.iam.system.model.query.DeptQuery;
import com.blkj.iam.system.model.vo.DeptPageVO;
import com.blkj.iam.system.model.vo.DeptVO;
import com.blkj.iam.system.model.vo.ShowDeptInfoVO;
import com.blkj.iam.system.service.DeptService;
import com.blkj.iam.system.service.ServerDataSyncService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 部门 业务实现类
 *
 * @author Ray
 * @since 2021/08/22
 */
@Service
@RequiredArgsConstructor
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements DeptService {

    private final DeptConverter deptConverter;
    private final DeptMapper deptMapper;
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final ServerDataSyncService dataSyncService;


    /**
     * 获取部门列表
     */
    @Override
    public List<DeptVO> getDeptList(DeptQuery queryParams) {
        // 查询参数
        String keywords = queryParams.getKeywords();
        Integer status = queryParams.getStatus();

        // 查询数据
        Long tenantId = SecurityUtils.getUser().get().getTenantId();
        List<Dept> deptList = this.list(new LambdaQueryWrapper<Dept>().eq(Dept::getTenantId, tenantId).like(StrUtil.isNotBlank(keywords), Dept::getName, keywords).eq(status != null, Dept::getStatus, status).orderByAsc(Dept::getSort));

        if (CollectionUtil.isEmpty(deptList)) {
            return Collections.EMPTY_LIST;
        }

        // 获取所有部门ID
        Set<Long> deptIds = deptList.stream().map(Dept::getId).collect(Collectors.toSet());
        // 获取父节点ID
        Set<Long> parentIds = deptList.stream().map(Dept::getParentId).collect(Collectors.toSet());
        // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级部门 O 作为根节点，因为部门筛选的时候 O 会被过滤掉
        List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);

        // 递归生成部门树形列表
        return rootIds.stream().flatMap(rootId -> recurDeptList(rootId, deptList).stream()).toList();
    }

    /**
     * 递归生成部门树形列表
     *
     * @param parentId 父ID
     * @param deptList 部门列表
     * @return 部门树形列表
     */
    public List<DeptVO> recurDeptList(Long parentId, List<Dept> deptList) {
        return deptList.stream().filter(dept -> dept.getParentId().equals(parentId)).map(dept -> {
            DeptVO deptVO = deptConverter.toVo(dept);
            List<DeptVO> children = recurDeptList(dept.getId(), deptList);
            deptVO.setChildren(children);
            return deptVO;
        }).toList();
    }

    /**
     * 部门下拉选项
     *
     * @return 部门下拉List集合
     */
    @Override
    public List<Option<Long>> listDeptOptions() {
        Long tenantId = SecurityUtils.getUser().get().getTenantId();

        List<Dept> deptList = this.list(new LambdaQueryWrapper<Dept>().eq(Dept::getStatus, StatusEnum.ENABLE.getValue()).eq(Dept::getTenantId, tenantId).select(Dept::getId, Dept::getParentId, Dept::getName).orderByAsc(Dept::getSort));
        if (CollectionUtil.isEmpty(deptList)) {
            return Collections.EMPTY_LIST;
        }

        Set<Long> deptIds = deptList.stream().map(Dept::getId).collect(Collectors.toSet());

        Set<Long> parentIds = deptList.stream().map(Dept::getParentId).collect(Collectors.toSet());

        List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);

        // 递归生成部门树形列表
        return rootIds.stream().flatMap(rootId -> recurDeptTreeOptions(rootId, deptList).stream()).toList();
    }

    /**
     * 新增部门
     *
     * @param formData 部门表单
     * @return 部门ID
     */
    @Override
    public Long saveDept(DeptForm formData) {
        // form->entity
        Dept entity = deptConverter.toEntity(formData);

        // 生成部门编号
        String deptCode = generateUniqueCode();
        entity.setCode(deptCode);
        Long deptId = SecurityUtils.getDeptId();
        Dept dept = deptMapper.selectById(deptId);
        entity.setCustomOrgCode(dept.getCode());

        // 处理监管部门ID数组
        List<String> deptIds = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(formData.getJgqIds())) {
            List<String> jgqIds = formData.getJgqIds();
            for (String jgqId : jgqIds) {
                Dept deptInfo = deptMapper.selectById(jgqId);
                if (ObjectUtil.isNotEmpty(deptInfo)) {
                    deptIds.add(deptInfo.getIdStr());
                }
            }
        }

        String collect = deptIds.stream().map(Object::toString).collect(Collectors.joining(", "));
        entity.setJgqIds(collect);

        // 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
        String treePath = generateDeptTreePath(formData.getParentId());
        entity.setTreePath(treePath);
        entity.setCreateBy(SecurityUtils.getUserId());
        entity.setCreateUser(SecurityUtils.getUsername());

        // 生成部门简称
        String output = PinyinUtils.toPinyinAbbreviation(entity.getName());
        entity.setNameAbbr(output);
        entity.setMineCode(output);
        if (ObjectUtil.isNotEmpty(formData.getDeptType())) {
            entity.setDepartType(formData.getDeptType().toString());
        }

        // 获取当前用户的租户
        Long tenantId = SecurityUtils.getUser().get().getTenantId();
        entity.setTenantId(tenantId);

        // 保存部门并返回部门ID
        boolean result = this.save(entity);
        Assert.isTrue(result, "部门保存失败");
        if (result) {
            // 同步数据到客户端
            dataSyncService.addDept(entity);
        }

        return entity.getId();
    }

    /**
     * 生成唯一的部门编号
     *
     * @return 唯一的部门编号
     */
    public String generateUniqueCode() {
        String deptCode;
        boolean isUnique;
        do {
            deptCode = generateCode();
            long count = this.count(new LambdaQueryWrapper<Dept>().eq(Dept::getCode, deptCode));
            isUnique = count == 0;
        } while (!isUnique);

        return deptCode;
    }

    /**
     * 获取部门表单
     *
     * @param deptId 部门ID
     * @return 部门表单对象
     */
    @Override
    public DeptForm getDeptForm(Long deptId) {
        Dept entity = this.getById(deptId);
        DeptForm form = deptConverter.toForm(entity);
        if (ObjectUtil.isNotEmpty(entity.getDepartType())) {
            form.setDeptType(Integer.valueOf(entity.getDepartType()));
        }
        List<String> idList = new ArrayList<>();
        String jgqIds = entity.getJgqIds();
        if (StrUtil.isNotBlank(jgqIds)) {
            String[] split = jgqIds.split(",");
            for (String idStr : split) {
                Dept dept = deptMapper.selectOne(new LambdaQueryWrapper<Dept>().eq(Dept::getIdStr, idStr.trim()));
                if (ObjectUtil.isNotEmpty(dept)) {
                    idList.add(dept.getId().toString());
                }
            }
        }
        form.setJgqIds(idList);
        return form;
    }


    /**
     * 更新部门
     *
     * @param deptId   部门ID
     * @param formData 部门表单
     * @return 部门ID
     */
    @Override
    public Long updateDept(Long deptId, DeptForm formData) {
        // 校验部门名称/部门编号是否存在
        String code = formData.getCode();
        long count = this.count(new LambdaQueryWrapper<Dept>().ne(Dept::getId, deptId).eq(Dept::getCode, code));
        Assert.isTrue(count == 0, "部门编号已存在");


        // form->entity
        Dept entity = deptConverter.toEntity(formData);
        entity.setId(deptId);

        // 处理监管部门ID数组
        List<String> deptIds = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(formData.getJgqIds())) {
            List<String> jgqIds = formData.getJgqIds();
            for (String jgqId : jgqIds) {
                Dept deptInfo = deptMapper.selectById(jgqId);
                if (ObjectUtil.isNotEmpty(deptInfo)) {
                    deptIds.add(deptInfo.getIdStr());
                }
            }
        }

        String collect = deptIds.stream().map(Object::toString).collect(Collectors.joining(", "));
        entity.setJgqIds(collect);

        // 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
        String treePath = generateDeptTreePath(formData.getParentId());
        entity.setTreePath(treePath);

        // 保存部门并返回部门ID
        boolean result = this.updateById(entity);
        Assert.isTrue(result, "部门更新失败");
        if (result) {
            // 同步数据到客户端
            dataSyncService.editDept(entity);
        }

        return entity.getId();
    }

    /**
     * 递归生成部门表格层级列表
     *
     * @param parentId 父ID
     * @param deptList 部门列表
     * @return 部门表格层级列表
     */
    public static List<Option<Long>> recurDeptTreeOptions(long parentId, List<Dept> deptList) {
        return CollectionUtil.emptyIfNull(deptList).stream().filter(dept -> dept.getParentId().equals(parentId)).map(dept -> {
            Option<Long> option = new Option<>(dept.getId(), dept.getName());
            List<Option<Long>> children = recurDeptTreeOptions(dept.getId(), deptList);
            if (CollectionUtil.isNotEmpty(children)) {
                option.setChildren(children);
            }
            return option;
        }).collect(Collectors.toList());
    }


    /**
     * 删除部门
     *
     * @param ids 部门ID，多个以英文逗号,拼接字符串
     * @return 是否删除成功
     */
    @Override
    public boolean deleteByIds(String ids) {
        // 删除部门及子部门
        if (StrUtil.isNotBlank(ids)) {
            String[] menuIds = ids.split(",");
            for (String deptId : menuIds) {
                Dept dept = deptMapper.selectById(deptId);
                ApiResult apiResult = dataSyncService.deleteDept(dept.getIdStr());
                if (apiResult.getCode() == 200) {
                    Long roleCount = roleMapper.selectCount(new LambdaQueryWrapper<Role>().eq(Role::getDepartId, dept.getId()).or().eq(Role::getDepartId, dept.getIdStr()));
                    if (roleCount > 0) {
                        throw new BusinessException("部门已关联角色，无法删除");
                    }

                    Long userCount = userMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getDeptId, dept.getId()).or().eq(User::getDeptId, dept.getIdStr()));
                    if (userCount > 0) {
                        throw new BusinessException("部门已关联用户，无法删除");
                    }

                    this.remove(new LambdaQueryWrapper<Dept>().eq(Dept::getId, deptId).or().eq(Dept::getParentId, deptId));
                }
            }
        }
        return true;
    }


    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    @Override
    public String generateDeptTreePath(Long parentId) {
        String treePath = null;
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            treePath = String.valueOf(parentId);
        } else {
            Dept parent = this.getById(parentId);
            if (parent != null) {
                treePath = parent.getTreePath() + "," + parent.getId();
            }
        }
        return treePath;
    }

    /***
     * 获取部门数据--客户端用
     * @param departId
     * @return: com.blkj.iam.system.model.vo.ShowDeptInfoVO
     */
    @Override
    public ShowDeptInfoVO getClientDeptInfo(String departId) {
        Dept dept = this.getOne(new LambdaQueryWrapper<Dept>().eq(Dept::getId, departId));
        ShowDeptInfoVO deptInfoVO = Dept.toShowDeptInfo(dept);
        return deptInfoVO;
    }

    /**
     * 获取用户部门数据
     *
     * @return: java.util.List<com.blkj.iam.common.model.Option < java.lang.Long>>
     */
    @Override
    public List<Option<Long>> listUserDeptOptions(int deptType) {
        Long tenantId = SecurityUtils.getUser().get().getTenantId();

        List<Dept> deptList = this.list(new LambdaQueryWrapper<Dept>().eq(Dept::getStatus, StatusEnum.ENABLE.getValue()).eq(Dept::getTenantId, tenantId).eq(Dept::getDepartType, deptType).select(Dept::getId, Dept::getName));
        List<Option<Long>> objects = CollectionUtil.emptyIfNull(deptList).stream().map(dept -> {
            Option<Long> option = new Option<>(dept.getId(), dept.getName());
            return option;
        }).toList();

        return objects;
    }

    /**
     * 部门列表
     *
     * @param queryParams
     * @return 部门列表
     */
    @Override
    public List<DeptVO> getStationDeptList(DeptQuery queryParams) {
        // 查询参数
        String keywords = queryParams.getKeywords();
        Integer status = queryParams.getStatus();
        Integer deptType = queryParams.getDeptType();

        // 查询数据
        List<Integer> paramsList = new ArrayList<>();
        paramsList.add(StationConstants.MERCHANT);
        paramsList.add(StationConstants.SUPPLIER);
        paramsList.add(StationConstants.CARRIER);

        Long tenantId = SecurityUtils.getUser().get().getTenantId();
        List<Dept> deptList = this.list(new LambdaQueryWrapper<Dept>().eq(Dept::getTenantId, tenantId).
                like(StrUtil.isNotBlank(keywords), Dept::getName, keywords).eq(status != null, Dept::getStatus, status).
                orderByAsc(Dept::getSort).eq(deptType != null, Dept::getDepartType, deptType).in(Dept::getDepartType, paramsList));


        // 转换为 DeptVO 列表
        return deptList.stream()
                .map(dept -> deptConverter.toVo(dept))
                .collect(Collectors.toList());
    }

    /**
     * 部门分页列表
     *
     * @param pageQuery
     * @return 部门分页列表
     */
    @Override
    public IPage<DeptPageVO> getStationDeptPage(DeptPageQuery pageQuery) {
        // 参数构建
        int pageNum = pageQuery.getPageNum();
        int pageSize = pageQuery.getPageSize();
        Page<UserBO> page = new Page<>(pageNum, pageSize);

        Long tenantId = SecurityUtils.getUser().get().getTenantId();
        pageQuery.setTenantId(tenantId);

        ArrayList<Integer> deptTypeList = new ArrayList<>();
        deptTypeList.add(StationConstants.MERCHANT);
        deptTypeList.add(StationConstants.SUPPLIER);
        deptTypeList.add(StationConstants.CARRIER);
        pageQuery.setDeptTypeList(deptTypeList);

        // 查询数据
        Page<Dept> deptPage = this.baseMapper.getStationDeptPage(page, pageQuery);

        // 实体转换
        return deptConverter.toPageVo(deptPage);
    }

    /**
     * 生成新的部门代码
     *
     * @param parentId 父部门 ID
     * @return 新的部门代码
     */
    public String generateDeptCode(Long parentId) {
        String maxCode;
        if (parentId == null || parentId == 0) {
            // 如果 parentId 为 null 或 0，查询根部门中最大的 code
            List<String> codeList = deptMapper.getMaxRootDeptCode();
            maxCode = PinyinUtils.filterDeptCodes(codeList, true);
        } else {
            // 如果 parentId 不为 0，查询该父部门下最大的子部门 code
            List<String> codeList = deptMapper.getMaxChildDeptCode(parentId);
            maxCode = PinyinUtils.filterDeptCodes(codeList, false);
            String parentCode = deptMapper.getDeptCodeById(parentId);
            if (maxCode.length() == 3) {
                maxCode = parentCode + maxCode;
            }
        }

        return PinyinUtils.incrementCode(maxCode);
    }

    /**
     * 生成新的部门代码
     *
     * @return 新的部门代码
     */
    public String generateCode() {
        // 创建一个随机数生成器
        Random random = new Random();
        // 生成一个四位随机数字
        int number = random.nextInt(10000);
        // 格式化为四位数字，不足四位时前面补零
        return "C" + String.format("%04d", number);
    }
}
